home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / cg_draw.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  45.6 KB  |  2,066 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // cg_draw.c -- draw all of the graphical elements during
  4. // active (after loading) gameplay
  5.  
  6. #include "cg_local.h"
  7.  
  8. // set in CG_ParseTeamInfo
  9. int sortedTeamPlayers[TEAM_MAXOVERLAY];
  10. int    numSortedTeamPlayers;
  11. int drawTeamOverlayModificationCount = -1;
  12.  
  13. /*
  14. ==============
  15. CG_DrawField
  16.  
  17. Draws large numbers for status bar and powerups
  18. ==============
  19. */
  20. static void CG_DrawField (int x, int y, int width, int value) {
  21.     char    num[16], *ptr;
  22.     int        l;
  23.     int        frame;
  24.  
  25.     if ( width < 1 ) {
  26.         return;
  27.     }
  28.  
  29.     // draw number string
  30.     if ( width > 5 ) {
  31.         width = 5;
  32.     }
  33.  
  34.     switch ( width ) {
  35.     case 1:
  36.         value = value > 9 ? 9 : value;
  37.         value = value < 0 ? 0 : value;
  38.         break;
  39.     case 2:
  40.         value = value > 99 ? 99 : value;
  41.         value = value < -9 ? -9 : value;
  42.         break;
  43.     case 3:
  44.         value = value > 999 ? 999 : value;
  45.         value = value < -99 ? -99 : value;
  46.         break;
  47.     case 4:
  48.         value = value > 9999 ? 9999 : value;
  49.         value = value < -999 ? -999 : value;
  50.         break;
  51.     }
  52.  
  53.     Com_sprintf (num, sizeof(num), "%i", value);
  54.     l = strlen(num);
  55.     if (l > width)
  56.         l = width;
  57.     x += 2 + CHAR_WIDTH*(width - l);
  58.  
  59.     ptr = num;
  60.     while (*ptr && l)
  61.     {
  62.         if (*ptr == '-')
  63.             frame = STAT_MINUS;
  64.         else
  65.             frame = *ptr -'0';
  66.  
  67.         CG_DrawPic( x,y, CHAR_WIDTH, CHAR_HEIGHT, cgs.media.numberShaders[frame] );
  68.         x += CHAR_WIDTH;
  69.         ptr++;
  70.         l--;
  71.     }
  72. }
  73.  
  74.  
  75. /*
  76. ================
  77. CG_Draw3DModel
  78.  
  79. ================
  80. */
  81. static void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ) {
  82.     refdef_t        refdef;
  83.     refEntity_t        ent;
  84.  
  85.     if ( !cg_draw3dIcons.integer || !cg_drawIcons.integer ) {
  86.         return;
  87.     }
  88.  
  89.     CG_AdjustFrom640( &x, &y, &w, &h );
  90.  
  91.     memset( &refdef, 0, sizeof( refdef ) );
  92.  
  93.     memset( &ent, 0, sizeof( ent ) );
  94.     AnglesToAxis( angles, ent.axis );
  95.     VectorCopy( origin, ent.origin );
  96.     ent.hModel = model;
  97.     ent.customSkin = skin;
  98.     ent.renderfx = RF_NOSHADOW;        // no stencil shadows
  99.  
  100.     refdef.rdflags = RDF_NOWORLDMODEL;
  101.  
  102.     AxisClear( refdef.viewaxis );
  103.  
  104.     refdef.fov_x = 30;
  105.     refdef.fov_y = 30;
  106.  
  107.     refdef.x = x;
  108.     refdef.y = y;
  109.     refdef.width = w;
  110.     refdef.height = h;
  111.  
  112.     refdef.time = cg.time;
  113.  
  114.     trap_R_ClearScene();
  115.     trap_R_AddRefEntityToScene( &ent );
  116.     trap_R_RenderScene( &refdef );
  117. }
  118.  
  119. /*
  120. ================
  121. CG_DrawHead
  122.  
  123. Used for both the status bar and the scoreboard
  124. ================
  125. */
  126. void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ) {
  127.     clipHandle_t    cm;
  128.     clientInfo_t    *ci;
  129.     float            len;
  130.     vec3_t            origin;
  131.     vec3_t            mins, maxs;
  132.  
  133.     ci = &cgs.clientinfo[ clientNum ];
  134.  
  135.     if ( cg_draw3dIcons.integer ) {
  136.         cm = ci->headModel;
  137.         if ( !cm ) {
  138.             return;
  139.         }
  140.  
  141.         // offset the origin y and z to center the head
  142.         trap_R_ModelBounds( cm, mins, maxs );
  143.  
  144.         origin[2] = -0.5 * ( mins[2] + maxs[2] );
  145.         origin[1] = 0.5 * ( mins[1] + maxs[1] );
  146.  
  147.         // calculate distance so the head nearly fills the box
  148.         // assume heads are taller than wide
  149.         len = 0.7 * ( maxs[2] - mins[2] );        
  150.         origin[0] = len / 0.268;    // len / tan( fov/2 )
  151.  
  152.         // allow per-model tweaking
  153.         VectorAdd( origin, ci->headOffset, origin );
  154.  
  155.         CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles );
  156.     } else if ( cg_drawIcons.integer ) {
  157.         CG_DrawPic( x, y, w, h, ci->modelIcon );
  158.     }
  159.  
  160.     // if they are deferred, draw a cross out
  161.     if ( ci->deferred ) {
  162.         CG_DrawPic( x, y, w, h, cgs.media.deferShader );
  163.     }
  164. }
  165.  
  166. /*
  167. ================
  168. CG_DrawFlagModel
  169.  
  170. Used for both the status bar and the scoreboard
  171. ================
  172. */
  173. void CG_DrawFlagModel( float x, float y, float w, float h, int team ) {
  174.     qhandle_t        cm;
  175.     float            len;
  176.     vec3_t            origin, angles;
  177.     vec3_t            mins, maxs;
  178.  
  179.     if ( cg_draw3dIcons.integer ) {
  180.  
  181.         VectorClear( angles );
  182.  
  183.         cm = cgs.media.redFlagModel;
  184.  
  185.         // offset the origin y and z to center the flag
  186.         trap_R_ModelBounds( cm, mins, maxs );
  187.  
  188.         origin[2] = -0.5 * ( mins[2] + maxs[2] );
  189.         origin[1] = 0.5 * ( mins[1] + maxs[1] );
  190.  
  191.         // calculate distance so the flag nearly fills the box
  192.         // assume heads are taller than wide
  193.         len = 0.5 * ( maxs[2] - mins[2] );        
  194.         origin[0] = len / 0.268;    // len / tan( fov/2 )
  195.  
  196.         angles[YAW] = 60 * sin( cg.time / 2000.0 );;
  197.  
  198.         CG_Draw3DModel( x, y, w, h, 
  199.             team == TEAM_RED ? cgs.media.redFlagModel : cgs.media.blueFlagModel, 
  200.             0, origin, angles );
  201.     } else if ( cg_drawIcons.integer ) {
  202.         gitem_t *item = BG_FindItemForPowerup( team == TEAM_RED ? PW_REDFLAG : PW_BLUEFLAG );
  203.  
  204.         CG_DrawPic( x, y, w, h, cg_items[ ITEM_INDEX(item) ].icon );
  205.     }
  206. }
  207.  
  208. /*
  209. ================
  210. CG_DrawStatusBarHead
  211.  
  212. ================
  213. */
  214. static void CG_DrawStatusBarHead( float x ) {
  215.     vec3_t        angles;
  216.     float        size, stretch;
  217.     float        frac;
  218.  
  219.     VectorClear( angles );
  220.  
  221.     if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) {
  222.         frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME;
  223.         size = ICON_SIZE * 1.25 * ( 1.5 - frac * 0.5 );
  224.  
  225.         stretch = size - ICON_SIZE * 1.25;
  226.         // kick in the direction of damage
  227.         x -= stretch * 0.5 + cg.damageX * stretch * 0.5;
  228.  
  229.         cg.headStartYaw = 180 + cg.damageX * 45;
  230.  
  231.         cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
  232.         cg.headEndPitch = 5 * cos( crandom()*M_PI );
  233.  
  234.         cg.headStartTime = cg.time;
  235.         cg.headEndTime = cg.time + 100 + random() * 2000;
  236.     } else {
  237.         if ( cg.time >= cg.headEndTime ) {
  238.             // select a new head angle
  239.             cg.headStartYaw = cg.headEndYaw;
  240.             cg.headStartPitch = cg.headEndPitch;
  241.             cg.headStartTime = cg.headEndTime;
  242.             cg.headEndTime = cg.time + 100 + random() * 2000;
  243.  
  244.             cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
  245.             cg.headEndPitch = 5 * cos( crandom()*M_PI );
  246.         }
  247.  
  248.         size = ICON_SIZE * 1.25;
  249.     }
  250.  
  251.     // if the server was frozen for a while we may have a bad head start time
  252.     if ( cg.headStartTime > cg.time ) {
  253.         cg.headStartTime = cg.time;
  254.     }
  255.  
  256.     frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime );
  257.     frac = frac * frac * ( 3 - 2 * frac );
  258.     angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac;
  259.     angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac;
  260.  
  261.     CG_DrawHead( x, 480 - size, size, size, 
  262.                 cg.snap->ps.clientNum, angles );
  263. }
  264.  
  265. /*
  266. ================
  267. CG_DrawStatusBarFlag
  268.  
  269. ================
  270. */
  271. static void CG_DrawStatusBarFlag( float x, int team ) {
  272.     CG_DrawFlagModel( x, 480 - ICON_SIZE, ICON_SIZE, ICON_SIZE, team );
  273. }
  274.  
  275.  
  276. /*
  277. ================
  278. CG_DrawTeamBackground
  279.  
  280. ================
  281. */
  282. void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team )
  283. {
  284.     vec4_t        hcolor;
  285.  
  286.     hcolor[3] = alpha;
  287.     if ( team == TEAM_RED ) {
  288.         hcolor[0] = 1;
  289.         hcolor[1] = 0;
  290.         hcolor[2] = 0;
  291.     } else if ( team == TEAM_BLUE ) {
  292.         hcolor[0] = 0;
  293.         hcolor[1] = 0;
  294.         hcolor[2] = 1;
  295.     } else {
  296.         return; // no team
  297.     }
  298.     trap_R_SetColor( hcolor );
  299.     CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
  300.     trap_R_SetColor( NULL );
  301. }
  302.  
  303. /*
  304. ================
  305. CG_DrawStatusBar
  306.  
  307. ================
  308. */
  309. static void CG_DrawStatusBar( void ) {
  310.     int            color;
  311.     centity_t    *cent;
  312.     playerState_t    *ps;
  313.     int            value;
  314.     vec4_t        hcolor;
  315.     vec3_t        angles;
  316.     vec3_t        origin;
  317.     static float colors[4][4] = { 
  318. //        { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} };
  319.         { 1, 0.69, 0, 1.0 } ,        // normal
  320.         { 1.0, 0.2, 0.2, 1.0 },        // low health
  321.         {0.5, 0.5, 0.5, 1},            // weapon firing
  322.         { 1, 1, 1, 1 } };            // health > 100
  323.  
  324.     if ( cg_drawStatus.integer == 0 ) {
  325.         return;
  326.     }
  327.  
  328.     // draw the team background
  329.     CG_DrawTeamBackground( 0, 420, 640, 60, 0.33, cg.snap->ps.persistant[PERS_TEAM] );
  330.  
  331.     cent = &cg_entities[cg.snap->ps.clientNum];
  332.     ps = &cg.snap->ps;
  333.  
  334.     VectorClear( angles );
  335.  
  336.     // draw any 3D icons first, so the changes back to 2D are minimized
  337.     if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) {
  338.         origin[0] = 70;
  339.         origin[1] = 0;
  340.         origin[2] = 0;
  341.         angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 );;
  342.         CG_Draw3DModel( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE,
  343.                        cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles );
  344.     }
  345.  
  346.     CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE );
  347.  
  348.     if (cg.predictedPlayerState.powerups[PW_REDFLAG])
  349.         CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_RED);
  350.     else if (cg.predictedPlayerState.powerups[PW_BLUEFLAG])
  351.         CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_BLUE);
  352.  
  353.     if ( ps->stats[ STAT_ARMOR ] ) {
  354.         origin[0] = 90;
  355.         origin[1] = 0;
  356.         origin[2] = -10;
  357.         angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
  358.         CG_Draw3DModel( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE,
  359.                        cgs.media.armorModel, 0, origin, angles );
  360.     }
  361.  
  362.     //
  363.     // ammo
  364.     //
  365.     if ( cent->currentState.weapon ) {
  366.         value = ps->ammo[cent->currentState.weapon];
  367.         if ( value > -1 ) {
  368.             if ( cg.predictedPlayerState.weaponstate == WEAPON_FIRING
  369.                 && cg.predictedPlayerState.weaponTime > 100 ) {
  370.                 // draw as dark grey when reloading
  371.                 color = 2;    // dark grey
  372.             } else {
  373.                 if ( value >= 0 ) {
  374.                     color = 0;    // green
  375.                 } else {
  376.                     color = 1;    // red
  377.                 }
  378.             }
  379.             trap_R_SetColor( colors[color] );
  380.             
  381.             CG_DrawField (0, 432, 3, value);
  382.             trap_R_SetColor( NULL );
  383.  
  384.             // if we didn't draw a 3D icon, draw a 2D icon for ammo
  385.             if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
  386.                 qhandle_t    icon;
  387.  
  388.                 icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon;
  389.                 if ( icon ) {
  390.                     CG_DrawPic( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, icon );
  391.                 }
  392.             }
  393.         }
  394.     }
  395.  
  396.     //
  397.     // health
  398.     //
  399.     value = ps->stats[STAT_HEALTH];
  400.     if ( value > 100 ) {
  401.         trap_R_SetColor( colors[3] );        // white
  402.     } else if (value > 25) {
  403.         trap_R_SetColor( colors[0] );    // green
  404.     } else if (value > 0) {
  405.         color = (cg.time >> 8) & 1;    // flash
  406.         trap_R_SetColor( colors[color] );
  407.     } else {
  408.         trap_R_SetColor( colors[1] );    // red
  409.     }
  410.  
  411.     // stretch the health up when taking damage
  412.     CG_DrawField ( 185, 432, 3, value);
  413.     CG_ColorForHealth( hcolor );
  414.     trap_R_SetColor( hcolor );
  415.  
  416.  
  417.     //
  418.     // armor
  419.     //
  420.     value = ps->stats[STAT_ARMOR];
  421.     if (value > 0 ) {
  422.         trap_R_SetColor( colors[0] );
  423.         CG_DrawField (370, 432, 3, value);
  424.         trap_R_SetColor( NULL );
  425.         // if we didn't draw a 3D icon, draw a 2D icon for armor
  426.         if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
  427.             CG_DrawPic( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorIcon );
  428.         }
  429.  
  430.     }
  431. }
  432.  
  433. /*
  434. ===========================================================================================
  435.  
  436.   UPPER RIGHT CORNER
  437.  
  438. ===========================================================================================
  439. */
  440.  
  441. /*
  442. ================
  443. CG_DrawAttacker
  444.  
  445. ================
  446. */
  447. static float CG_DrawAttacker( float y ) {
  448.     int            t;
  449.     float        size;
  450.     vec3_t        angles;
  451.     const char    *info;
  452.     const char    *name;
  453.     int            clientNum;
  454.  
  455.     if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
  456.         return y;
  457.     }
  458.  
  459.     if ( !cg.attackerTime ) {
  460.         return y;
  461.     }
  462.  
  463.     clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER];
  464.     if ( clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum ) {
  465.         return y;
  466.     }
  467.  
  468.     t = cg.time - cg.attackerTime;
  469.     if ( t > ATTACKER_HEAD_TIME ) {
  470.         cg.attackerTime = 0;
  471.         return y;
  472.     }
  473.  
  474.     size = ICON_SIZE * 1.25;
  475.  
  476.     angles[PITCH] = 0;
  477.     angles[YAW] = 180;
  478.     angles[ROLL] = 0;
  479.     CG_DrawHead( 640 - size, y, size, size, clientNum, angles );
  480.  
  481.     info = CG_ConfigString( CS_PLAYERS + clientNum );
  482.     name = Info_ValueForKey(  info, "n" );
  483.     y += size;
  484.     CG_DrawBigString( 640 - ( Q_PrintStrlen( name ) * BIGCHAR_WIDTH), y, name, 0.5 );
  485.  
  486.     return y + BIGCHAR_HEIGHT + 2;
  487. }
  488.  
  489. /*
  490. ==================
  491. CG_DrawSnapshot
  492. ==================
  493. */
  494. static float CG_DrawSnapshot( float y ) {
  495.     char        *s;
  496.     int            w;
  497.  
  498.     s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime, 
  499.         cg.latestSnapshotNum, cgs.serverCommandSequence );
  500.     w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
  501.  
  502.     CG_DrawBigString( 635 - w, y + 2, s, 1.0F);
  503.  
  504.     return y + BIGCHAR_HEIGHT + 4;
  505. }
  506.  
  507. /*
  508. ==================
  509. CG_DrawFPS
  510. ==================
  511. */
  512. #define    FPS_FRAMES    4
  513. static float CG_DrawFPS( float y ) {
  514.     char        *s;
  515.     int            w;
  516.     static int    previousTimes[FPS_FRAMES];
  517.     static int    index;
  518.     int        i, total;
  519.     int        fps;
  520.     static    int    previous;
  521.     int        t, frameTime;
  522.  
  523.     // don't use serverTime, because that will be drifting to
  524.     // correct for internet lag changes, timescales, timedemos, etc
  525.     t = trap_Milliseconds();
  526.     frameTime = t - previous;
  527.     previous = t;
  528.  
  529.     previousTimes[index % FPS_FRAMES] = frameTime;
  530.     index++;
  531.     if ( index > FPS_FRAMES ) {
  532.         // average multiple frames together to smooth changes out a bit
  533.         total = 0;
  534.         for ( i = 0 ; i < FPS_FRAMES ; i++ ) {
  535.             total += previousTimes[i];
  536.         }
  537.         if ( !total ) {
  538.             total = 1;
  539.         }
  540.         fps = 1000 * FPS_FRAMES / total;
  541.  
  542.         s = va( "%ifps", fps );
  543.         w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
  544.  
  545.         CG_DrawBigString( 635 - w, y + 2, s, 1.0F);
  546.     }
  547.  
  548.     return y + BIGCHAR_HEIGHT + 4;
  549. }
  550.  
  551. /*
  552. =================
  553. CG_DrawTimer
  554. =================
  555. */
  556. static float CG_DrawTimer( float y ) {
  557.     char        *s;
  558.     int            w;
  559.     int            mins, seconds, tens;
  560.     int            msec;
  561.  
  562.     msec = cg.time - cgs.levelStartTime;
  563.  
  564.     seconds = msec / 1000;
  565.     mins = seconds / 60;
  566.     seconds -= mins * 60;
  567.     tens = seconds / 10;
  568.     seconds -= tens * 10;
  569.  
  570.     s = va( "%i:%i%i", mins, tens, seconds );
  571.     w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
  572.  
  573.     CG_DrawBigString( 635 - w, y + 2, s, 1.0F);
  574.  
  575.     return y + BIGCHAR_HEIGHT + 4;
  576. }
  577.  
  578.  
  579. /*
  580. =================
  581. CG_DrawTeamOverlay
  582. =================
  583. */
  584.  
  585. #define TEAM_OVERLAY_MAXNAME_WIDTH    12
  586. #define TEAM_OVERLAY_MAXLOCATION_WIDTH    16
  587.  
  588. static float CG_DrawTeamOverlay( float y, qboolean right, qboolean upper ) {
  589.     int x, w, h, xx;
  590.     int i, j, len;
  591.     const char *p;
  592.     vec4_t        hcolor;
  593.     int pwidth, lwidth;
  594.     int plyrs;
  595.     char st[16];
  596.     clientInfo_t *ci;
  597.     int ret_y;
  598.  
  599.     if ( !cg_drawTeamOverlay.integer ) {
  600.         return y;
  601.     }
  602.  
  603.     if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_RED &&
  604.         cg.snap->ps.persistant[PERS_TEAM] != TEAM_BLUE )
  605.         return y; // Not on any team
  606.  
  607.     plyrs = 0;
  608.  
  609.     // max player name width
  610.     pwidth = 0;
  611.     for (i = 0; i < numSortedTeamPlayers; i++) {
  612.         ci = cgs.clientinfo + sortedTeamPlayers[i];
  613.         if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {
  614.             plyrs++;
  615.             len = CG_DrawStrlen(ci->name);
  616.             if (len > pwidth)
  617.                 pwidth = len;
  618.         }
  619.     }
  620.  
  621.     if (!plyrs)
  622.         return y;
  623.  
  624.     if (pwidth > TEAM_OVERLAY_MAXNAME_WIDTH)
  625.         pwidth = TEAM_OVERLAY_MAXNAME_WIDTH;
  626.  
  627.     // max location name width
  628.     lwidth = 0;
  629.     for (i = 1; i < MAX_LOCATIONS; i++) {
  630.         p = CG_ConfigString(CS_LOCATIONS + i);
  631.         if (p && *p) {
  632.             len = CG_DrawStrlen(p);
  633.             if (len > lwidth)
  634.                 lwidth = len;
  635.         }
  636.     }
  637.  
  638.     if (lwidth > TEAM_OVERLAY_MAXLOCATION_WIDTH)
  639.         lwidth = TEAM_OVERLAY_MAXLOCATION_WIDTH;
  640.  
  641.     w = (pwidth + lwidth + 4 + 7) * TINYCHAR_WIDTH;
  642.  
  643.     if ( right )
  644.         x = 640 - w;
  645.     else
  646.         x = 0;
  647.  
  648.     h = plyrs * TINYCHAR_HEIGHT;
  649.  
  650.     if ( upper ) {
  651.         ret_y = y + h;
  652.     } else {
  653.         y -= h;
  654.         ret_y = y;
  655.     }
  656.  
  657.     if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
  658.         hcolor[0] = 1;
  659.         hcolor[1] = 0;
  660.         hcolor[2] = 0;
  661.         hcolor[3] = 0.33;
  662.     } else { // if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE )
  663.         hcolor[0] = 0;
  664.         hcolor[1] = 0;
  665.         hcolor[2] = 1;
  666.         hcolor[3] = 0.33;
  667.     }
  668.     trap_R_SetColor( hcolor );
  669.     CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
  670.     trap_R_SetColor( NULL );
  671.  
  672.     for (i = 0; i < numSortedTeamPlayers; i++) {
  673.         ci = cgs.clientinfo + sortedTeamPlayers[i];
  674.         if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {
  675.  
  676.             hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0;
  677.  
  678.             xx = x + TINYCHAR_WIDTH;
  679.  
  680.             CG_DrawStringExt( xx, y,
  681.                 ci->name, hcolor, qfalse, qfalse,
  682.                 TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXNAME_WIDTH);
  683.  
  684.             if (lwidth) {
  685.                 p = CG_ConfigString(CS_LOCATIONS + ci->location);
  686.                 if (!p || !*p)
  687.                     p = "unknown";
  688.                 len = CG_DrawStrlen(p);
  689.                 if (len > lwidth)
  690.                     len = lwidth;
  691.  
  692. //                xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth + 
  693. //                    ((lwidth/2 - len/2) * TINYCHAR_WIDTH);
  694.                 xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth;
  695.                 CG_DrawStringExt( xx, y,
  696.                     p, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
  697.                     TEAM_OVERLAY_MAXLOCATION_WIDTH);
  698.             }
  699.  
  700.             CG_GetColorForHealth( ci->health, ci->armor, hcolor );
  701.  
  702.             Com_sprintf (st, sizeof(st), "%3i %3i", ci->health,    ci->armor);
  703.  
  704.             xx = x + TINYCHAR_WIDTH * 3 + 
  705.                 TINYCHAR_WIDTH * pwidth + TINYCHAR_WIDTH * lwidth;
  706.  
  707.             CG_DrawStringExt( xx, y,
  708.                 st, hcolor, qfalse, qfalse,
  709.                 TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 );
  710.  
  711.             // draw weapon icon
  712.             xx += TINYCHAR_WIDTH * 3;
  713.  
  714.             if ( cg_weapons[ci->curWeapon].weaponIcon ) {
  715.                 CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 
  716.                     cg_weapons[ci->curWeapon].weaponIcon );
  717.             } else {
  718.                 CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 
  719.                     cgs.media.deferShader );
  720.             }
  721.  
  722.             // Draw powerup icons
  723.             if (right) {
  724.                 xx = x;
  725.             } else {
  726.                 xx = x + w - TINYCHAR_WIDTH;
  727.             }
  728.             for (j = 0; j < PW_NUM_POWERUPS; j++) {
  729.                 if (ci->powerups & (1 << j)) {
  730.                     gitem_t    *item;
  731.  
  732.                     item = BG_FindItemForPowerup( j );
  733.  
  734.                     CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 
  735.                         trap_R_RegisterShader( item->icon ) );
  736.                     if (right) {
  737.                         xx -= TINYCHAR_WIDTH;
  738.                     } else {
  739.                         xx += TINYCHAR_WIDTH;
  740.                     }
  741.                 }
  742.             }
  743.  
  744.             y += TINYCHAR_HEIGHT;
  745.         }
  746.     }
  747.  
  748.     return ret_y;
  749. }
  750.  
  751.  
  752. /*
  753. =====================
  754. CG_DrawUpperRight
  755.  
  756. =====================
  757. */
  758. static void CG_DrawUpperRight( void ) {
  759.     float    y;
  760.  
  761.     y = 0;
  762.  
  763.     if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 1 ) {
  764.         y = CG_DrawTeamOverlay( y, qtrue, qtrue );
  765.     } 
  766.     if ( cg_drawSnapshot.integer ) {
  767.         y = CG_DrawSnapshot( y );
  768.     }
  769.     if ( cg_drawFPS.integer ) {
  770.         y = CG_DrawFPS( y );
  771.     }
  772.     if ( cg_drawTimer.integer ) {
  773.         y = CG_DrawTimer( y );
  774.     }
  775.     if ( cg_drawAttacker.integer ) {
  776.         y = CG_DrawAttacker( y );
  777.     }
  778.  
  779. }
  780.  
  781. /*
  782. ===========================================================================================
  783.  
  784.   LOWER RIGHT CORNER
  785.  
  786. ===========================================================================================
  787. */
  788.  
  789. /*
  790. =================
  791. CG_DrawScores
  792.  
  793. Draw the small two score display
  794. =================
  795. */
  796. static float CG_DrawScores( float y ) {
  797.     const char    *s;
  798.     int            s1, s2, score;
  799.     int            x, w;
  800.     int            v;
  801.     vec4_t        color;
  802.     float        y1;
  803.     gitem_t        *item;
  804.  
  805.     s1 = cgs.scores1;
  806.     s2 = cgs.scores2;
  807.  
  808.     y -=  BIGCHAR_HEIGHT + 8;
  809.  
  810.     y1 = y;
  811.  
  812.     // draw from the right side to left
  813.     if ( cgs.gametype >= GT_TEAM ) {
  814.         x = 640;
  815.  
  816.         color[0] = 0;
  817.         color[1] = 0;
  818.         color[2] = 1;
  819.         color[3] = 0.33;
  820.         s = va( "%2i", s2 );
  821.         w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
  822.         x -= w;
  823.         CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
  824.         if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
  825.             CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
  826.         }
  827.         CG_DrawBigString( x + 4, y, s, 1.0F);
  828.  
  829.         if ( cgs.gametype == GT_CTF ) {
  830.             // Display flag status
  831.             item = BG_FindItemForPowerup( PW_BLUEFLAG );
  832.  
  833.             if (item) {
  834.                 y1 = y - BIGCHAR_HEIGHT - 8;
  835.                 if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) {
  836.                     CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.blueFlagShader[cgs.blueflag] );
  837.                 }
  838. #if 0
  839.                 CG_RegisterItemVisuals( ITEM_INDEX(item) );
  840.                 switch (cgs.blueflag) {
  841.                 case 0 :  // at base
  842.                     // Draw the icon
  843.                     CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, trap_R_RegisterShader( item->icon ) );
  844.                 case 1 : // taken
  845.                     CG_FillRect( x, y1-4,  w, BIGCHAR_HEIGHT+8, color );
  846.                     break;
  847.                 case 2 : // droppped, grey background
  848.                     color[0] = color[1] = color[2] = 1;
  849.                     CG_FillRect( x, y1-4,  w, BIGCHAR_HEIGHT+8, color );
  850.                     break;
  851.                 // Other values won't draw (-1 is disabled)
  852.                 }
  853. #endif
  854.             }
  855.         }
  856.  
  857.         color[0] = 1;
  858.         color[1] = 0;
  859.         color[2] = 0;
  860.         color[3] = 0.33;
  861.         s = va( "%2i", s1 );
  862.         w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
  863.         x -= w;
  864.         CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
  865.         if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
  866.             CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
  867.         }
  868.         CG_DrawBigString( x + 4, y, s, 1.0F);
  869.  
  870.         if ( cgs.gametype == GT_CTF ) {
  871.             // Display flag status
  872.             item = BG_FindItemForPowerup( PW_REDFLAG );
  873.  
  874.             if (item) {
  875.                 y1 = y - BIGCHAR_HEIGHT - 8;
  876.                 if( cgs.redflag >= 0 && cgs.redflag <= 2 ) {
  877.                     CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.redFlagShader[cgs.redflag] );
  878.                 }
  879. #if 0
  880.                 CG_RegisterItemVisuals( ITEM_INDEX(item) );
  881.                 switch (cgs.redflag) {
  882.                 case 0 :  // at base
  883.                     // Draw the icon
  884.                     CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, trap_R_RegisterShader( item->icon ) );
  885.                     break;
  886.                 case 1 : // taken
  887.                     CG_FillRect( x, y1-4,  w, BIGCHAR_HEIGHT+8, color );
  888.                     break;
  889.                 case 2 : // droppped, grey background
  890.                     color[0] = color[1] = color[2] = 1;
  891.                     CG_FillRect( x, y1-4,  w, BIGCHAR_HEIGHT+8, color );
  892.                     break;
  893.                 // Other values won't draw (-1 is disabled)
  894.                 }
  895. #endif
  896.             }
  897.         }
  898.  
  899.  
  900.         if ( cgs.gametype == GT_CTF ) {
  901.             v = cgs.capturelimit;
  902.         } else {
  903.             v = cgs.fraglimit;
  904.         }
  905.         if ( v ) {
  906.             s = va( "%2i", v );
  907.             w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
  908.             x -= w;
  909.             CG_DrawBigString( x + 4, y, s, 1.0F);
  910.         }
  911.  
  912.     } else {
  913.         qboolean    spectator;
  914.  
  915.         x = 640;
  916.         score = cg.snap->ps.persistant[PERS_SCORE];
  917.         spectator = ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR );
  918.  
  919.         // always show your score in the second box if not in first place
  920.         if ( s1 != score ) {
  921.             s2 = score;
  922.         }
  923.         if ( s2 != SCORE_NOT_PRESENT ) {
  924.             s = va( "%2i", s2 );
  925.             w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
  926.             x -= w;
  927.             if ( !spectator && score == s2 && score != s1 ) {
  928.                 color[0] = 1;
  929.                 color[1] = 0;
  930.                 color[2] = 0;
  931.                 color[3] = 0.33;
  932.                 CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
  933.                 CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
  934.             } else {
  935.                 color[0] = 0.5;
  936.                 color[1] = 0.5;
  937.                 color[2] = 0.5;
  938.                 color[3] = 0.33;
  939.                 CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
  940.             }    
  941.             CG_DrawBigString( x + 4, y, s, 1.0F);
  942.         }
  943.  
  944.         // first place
  945.         if ( s1 != SCORE_NOT_PRESENT ) {
  946.             s = va( "%2i", s1 );
  947.             w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
  948.             x -= w;
  949.             if ( !spectator && score == s1 ) {
  950.                 color[0] = 0;
  951.                 color[1] = 0;
  952.                 color[2] = 1;
  953.                 color[3] = 0.33;
  954.                 CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
  955.                 CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
  956.             } else {
  957.                 color[0] = 0.5;
  958.                 color[1] = 0.5;
  959.                 color[2] = 0.5;
  960.                 color[3] = 0.33;
  961.                 CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
  962.             }    
  963.             CG_DrawBigString( x + 4, y, s, 1.0F);
  964.         }
  965.  
  966.         if ( cgs.fraglimit ) {
  967.             s = va( "%2i", cgs.fraglimit );
  968.             w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
  969.             x -= w;
  970.             CG_DrawBigString( x + 4, y, s, 1.0F);
  971.         }
  972.  
  973.     }
  974.  
  975.     return y1 - 8;
  976. }
  977.  
  978. /*
  979. ================
  980. CG_DrawPowerups
  981. ================
  982. */
  983. static float CG_DrawPowerups( float y ) {
  984.     int        sorted[MAX_POWERUPS];
  985.     int        sortedTime[MAX_POWERUPS];
  986.     int        i, j, k;
  987.     int        active;
  988.     playerState_t    *ps;
  989.     int        t;
  990.     gitem_t    *item;
  991.     int        x;
  992.     int        color;
  993.     float    size;
  994.     float    f;
  995.     static float colors[2][4] = { 
  996.         { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 } };
  997.  
  998.     ps = &cg.snap->ps;
  999.  
  1000.     if ( ps->stats[STAT_HEALTH] <= 0 ) {
  1001.         return y;
  1002.     }
  1003.  
  1004.     // sort the list by time remaining
  1005.     active = 0;
  1006.     for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
  1007.         if ( !ps->powerups[ i ] ) {
  1008.             continue;
  1009.         }
  1010.         t = ps->powerups[ i ] - cg.time;
  1011.         // ZOID--don't draw if the power up has unlimited time (999 seconds)
  1012.         // This is true of the CTF flags
  1013.         if ( t < 0 || t > 999000) {
  1014.             continue;
  1015.         }
  1016.  
  1017.         // insert into the list
  1018.         for ( j = 0 ; j < active ; j++ ) {
  1019.             if ( sortedTime[j] >= t ) {
  1020.                 for ( k = active - 1 ; k >= j ; k-- ) {
  1021.                     sorted[k+1] = sorted[k];
  1022.                     sortedTime[k+1] = sortedTime[k];
  1023.                 }
  1024.                 break;
  1025.             }
  1026.         }
  1027.         sorted[j] = i;
  1028.         sortedTime[j] = t;
  1029.         active++;
  1030.     }
  1031.  
  1032.     // draw the icons and timers
  1033.     x = 640 - ICON_SIZE - CHAR_WIDTH * 2;
  1034.     for ( i = 0 ; i < active ; i++ ) {
  1035.         item = BG_FindItemForPowerup( sorted[i] );
  1036.  
  1037.         color = 1;
  1038.  
  1039.         y -= ICON_SIZE;
  1040.  
  1041.         trap_R_SetColor( colors[color] );
  1042.         CG_DrawField( x, y, 2, sortedTime[ i ] / 1000 );
  1043.  
  1044.         t = ps->powerups[ sorted[i] ];
  1045.         if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
  1046.             trap_R_SetColor( NULL );
  1047.         } else {
  1048.             vec4_t    modulate;
  1049.  
  1050.             f = (float)( t - cg.time ) / POWERUP_BLINK_TIME;
  1051.             f -= (int)f;
  1052.             modulate[0] = modulate[1] = modulate[2] = modulate[3] = f;
  1053.             trap_R_SetColor( modulate );
  1054.         }
  1055.  
  1056.         if ( cg.powerupActive == sorted[i] && 
  1057.             cg.time - cg.powerupTime < PULSE_TIME ) {
  1058.             f = 1.0 - ( ( (float)cg.time - cg.powerupTime ) / PULSE_TIME );
  1059.             size = ICON_SIZE * ( 1.0 + ( PULSE_SCALE - 1.0 ) * f );
  1060.         } else {
  1061.             size = ICON_SIZE;
  1062.         }
  1063.  
  1064.         CG_DrawPic( 640 - size, y + ICON_SIZE / 2 - size / 2, 
  1065.             size, size, trap_R_RegisterShader( item->icon ) );
  1066.     }
  1067.     trap_R_SetColor( NULL );
  1068.  
  1069.     return y;
  1070. }
  1071.  
  1072.  
  1073. /*
  1074. =====================
  1075. CG_DrawLowerRight
  1076.  
  1077. =====================
  1078. */
  1079. static void CG_DrawLowerRight( void ) {
  1080.     float    y;
  1081.  
  1082.     y = 480 - ICON_SIZE;
  1083.  
  1084.     if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) {
  1085.         y = CG_DrawTeamOverlay( y, qtrue, qfalse );
  1086.     } 
  1087.  
  1088.     y = CG_DrawScores( y );
  1089.     y = CG_DrawPowerups( y );
  1090. }
  1091.  
  1092. /*
  1093. ===================
  1094. CG_DrawPickupItem
  1095. ===================
  1096. */
  1097. static int CG_DrawPickupItem( int y ) {
  1098.     int        value;
  1099.     float    *fadeColor;
  1100.  
  1101.     if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
  1102.         return y;
  1103.     }
  1104.  
  1105.     y -= ICON_SIZE;
  1106.  
  1107.     value = cg.itemPickup;
  1108.     if ( value ) {
  1109.         fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 );
  1110.         if ( fadeColor ) {
  1111.             CG_RegisterItemVisuals( value );
  1112.             trap_R_SetColor( fadeColor );
  1113.             CG_DrawPic( 8, y, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
  1114.             CG_DrawBigString( ICON_SIZE + 16, y + (ICON_SIZE/2 - BIGCHAR_HEIGHT/2), bg_itemlist[ value ].pickup_name, fadeColor[0] );
  1115.             trap_R_SetColor( NULL );
  1116.         }
  1117.     }
  1118.     
  1119.     return y;
  1120. }
  1121.  
  1122. /*
  1123. =====================
  1124. CG_DrawLowerLeft
  1125.  
  1126. =====================
  1127. */
  1128. static void CG_DrawLowerLeft( void ) {
  1129.     float    y;
  1130.  
  1131.     y = 480 - ICON_SIZE;
  1132.  
  1133.     if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3 ) {
  1134.         y = CG_DrawTeamOverlay( y, qfalse, qfalse );
  1135.     } 
  1136.  
  1137.  
  1138.     y = CG_DrawPickupItem( y );
  1139. }
  1140.  
  1141.  
  1142.  
  1143. //===========================================================================================
  1144.  
  1145. /*
  1146. =================
  1147. CG_DrawTeamInfo
  1148. =================
  1149. */
  1150. static void CG_DrawTeamInfo( void ) {
  1151.     int w, h;
  1152.     int i, len;
  1153.     vec4_t        hcolor;
  1154.     int        chatHeight;
  1155.  
  1156. #define CHATLOC_Y 420 // bottom end
  1157. #define CHATLOC_X 0
  1158.  
  1159.     if (cg_teamChatHeight.integer < TEAMCHAT_HEIGHT)
  1160.         chatHeight = cg_teamChatHeight.integer;
  1161.     else
  1162.         chatHeight = TEAMCHAT_HEIGHT;
  1163.     if (chatHeight <= 0)
  1164.         return; // disabled
  1165.  
  1166.     if (cgs.teamLastChatPos != cgs.teamChatPos) {
  1167.         if (cg.time - cgs.teamChatMsgTimes[cgs.teamLastChatPos % chatHeight] > cg_teamChatTime.integer) {
  1168.             cgs.teamLastChatPos++;
  1169.         }
  1170.  
  1171.         h = (cgs.teamChatPos - cgs.teamLastChatPos) * TINYCHAR_HEIGHT;
  1172.  
  1173.         w = 0;
  1174.  
  1175.         for (i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++) {
  1176.             len = CG_DrawStrlen(cgs.teamChatMsgs[i % chatHeight]);
  1177.             if (len > w)
  1178.                 w = len;
  1179.         }
  1180.         w *= TINYCHAR_WIDTH;
  1181.         w += TINYCHAR_WIDTH * 2;
  1182.  
  1183.         if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
  1184.             hcolor[0] = 1;
  1185.             hcolor[1] = 0;
  1186.             hcolor[2] = 0;
  1187.             hcolor[3] = 0.33;
  1188.         } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
  1189.             hcolor[0] = 0;
  1190.             hcolor[1] = 0;
  1191.             hcolor[2] = 1;
  1192.             hcolor[3] = 0.33;
  1193.         } else {
  1194.             hcolor[0] = 0;
  1195.             hcolor[1] = 1;
  1196.             hcolor[2] = 0;
  1197.             hcolor[3] = 0.33;
  1198.         }
  1199.  
  1200.         trap_R_SetColor( hcolor );
  1201.         CG_DrawPic( CHATLOC_X, CHATLOC_Y - h, 640, h, cgs.media.teamStatusBar );
  1202.         trap_R_SetColor( NULL );
  1203.  
  1204.         hcolor[0] = hcolor[1] = hcolor[2] = 1.0;
  1205.         hcolor[3] = 1.0;
  1206.  
  1207.         for (i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i--) {
  1208.             CG_DrawStringExt( CHATLOC_X + TINYCHAR_WIDTH, 
  1209.                 CHATLOC_Y - (cgs.teamChatPos - i)*TINYCHAR_HEIGHT, 
  1210.                 cgs.teamChatMsgs[i % chatHeight], hcolor, qfalse, qfalse,
  1211.                 TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 );
  1212.         }
  1213.     }
  1214. }
  1215.  
  1216. /*
  1217. ===================
  1218. CG_DrawHoldableItem
  1219. ===================
  1220. */
  1221. static void CG_DrawHoldableItem( void ) { 
  1222.     int        value;
  1223.  
  1224.     value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM];
  1225.     if ( value ) {
  1226.         CG_RegisterItemVisuals( value );
  1227.         CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
  1228.     }
  1229.  
  1230. }
  1231.  
  1232.  
  1233. /*
  1234. ===================
  1235. CG_DrawReward
  1236. ===================
  1237. */
  1238. static void CG_DrawReward( void ) { 
  1239.     float    *color;
  1240.     int        i;
  1241.     float    x, y;
  1242.  
  1243.     if ( !cg_drawRewards.integer ) {
  1244.         return;
  1245.     }
  1246.     color = CG_FadeColor( cg.rewardTime, REWARD_TIME );
  1247.     if ( !color ) {
  1248.         return;
  1249.     }
  1250.  
  1251.     trap_R_SetColor( color );
  1252.     y = 56;
  1253.     x = 320 - cg.rewardCount * ICON_SIZE/2;
  1254.     for ( i = 0 ; i < cg.rewardCount ; i++ ) {
  1255.         CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader );
  1256.         x += ICON_SIZE;
  1257.     }
  1258.     trap_R_SetColor( NULL );
  1259. }
  1260.  
  1261.  
  1262. /*
  1263. ===============================================================================
  1264.  
  1265. LAGOMETER
  1266.  
  1267. ===============================================================================
  1268. */
  1269.  
  1270. #define    LAG_SAMPLES        128
  1271.  
  1272.  
  1273. typedef struct {
  1274.     int        frameSamples[LAG_SAMPLES];
  1275.     int        frameCount;
  1276.     int        snapshotFlags[LAG_SAMPLES];
  1277.     int        snapshotSamples[LAG_SAMPLES];
  1278.     int        snapshotCount;
  1279. } lagometer_t;
  1280.  
  1281. lagometer_t        lagometer;
  1282.  
  1283. /*
  1284. ==============
  1285. CG_AddLagometerFrameInfo
  1286.  
  1287. Adds the current interpolate / extrapolate bar for this frame
  1288. ==============
  1289. */
  1290. void CG_AddLagometerFrameInfo( void ) {
  1291.     int            offset;
  1292.  
  1293.     offset = cg.time - cg.latestSnapshotTime;
  1294.     lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1) ] = offset;
  1295.     lagometer.frameCount++;
  1296. }
  1297.  
  1298. /*
  1299. ==============
  1300. CG_AddLagometerSnapshotInfo
  1301.  
  1302. Each time a snapshot is received, log its ping time and
  1303. the number of snapshots that were dropped before it.
  1304.  
  1305. Pass NULL for a dropped packet.
  1306. ==============
  1307. */
  1308. void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) {
  1309.     // dropped packet
  1310.     if ( !snap ) {
  1311.         lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = -1;
  1312.         lagometer.snapshotCount++;
  1313.         return;
  1314.     }
  1315.  
  1316.     // add this snapshot's info
  1317.     lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->ping;
  1318.     lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->snapFlags;
  1319.     lagometer.snapshotCount++;
  1320. }
  1321.  
  1322. /*
  1323. ==============
  1324. CG_DrawDisconnect
  1325.  
  1326. Should we draw something differnet for long lag vs no packets?
  1327. ==============
  1328. */
  1329. static void CG_DrawDisconnect( void ) {
  1330.     float        x, y;
  1331.     int            cmdNum;
  1332.     usercmd_t    cmd;
  1333.     const char        *s;
  1334.     int            w;
  1335.  
  1336.     // draw the phone jack if we are completely past our buffers
  1337.     cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1;
  1338.     trap_GetUserCmd( cmdNum, &cmd );
  1339.     if ( cmd.serverTime <= cg.snap->ps.commandTime
  1340.         || cmd.serverTime > cg.time ) {    // special check for map_restart
  1341.         return;
  1342.     }
  1343.  
  1344.     // also add text in center of screen
  1345.     s = "Connection Interrupted";
  1346.     w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
  1347.     CG_DrawBigString( 320 - w/2, 100, s, 1.0F);
  1348.  
  1349.     // blink the icon
  1350.     if ( ( cg.time >> 9 ) & 1 ) {
  1351.         return;
  1352.     }
  1353.  
  1354.     x = 640 - 48;
  1355.     y = 480 - 48;
  1356.  
  1357.     CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) );
  1358. }
  1359.  
  1360.  
  1361. #define    MAX_LAGOMETER_PING    900
  1362. #define    MAX_LAGOMETER_RANGE    300
  1363.  
  1364. /*
  1365. ==============
  1366. CG_DrawLagometer
  1367. ==============
  1368. */
  1369. static void CG_DrawLagometer( void ) {
  1370.     int        a, x, y, i;
  1371.     float    v;
  1372.     float    ax, ay, aw, ah, mid, range;
  1373.     int        color;
  1374.     float    vscale;
  1375.  
  1376.     if ( !cg_lagometer.integer || cgs.localServer ) {
  1377.         CG_DrawDisconnect();
  1378.         return;
  1379.     }
  1380.  
  1381.     //
  1382.     // draw the graph
  1383.     //
  1384.     x = 640 - 48;
  1385.     y = 480 - 48;
  1386.  
  1387.     trap_R_SetColor( NULL );
  1388.     CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader );
  1389.  
  1390.     ax = x;
  1391.     ay = y;
  1392.     aw = 48;
  1393.     ah = 48;
  1394.     CG_AdjustFrom640( &ax, &ay, &aw, &ah );
  1395.  
  1396.     color = -1;
  1397.     range = ah / 3;
  1398.     mid = ay + range;
  1399.  
  1400.     vscale = range / MAX_LAGOMETER_RANGE;
  1401.  
  1402.     // draw the frame interpoalte / extrapolate graph
  1403.     for ( a = 0 ; a < aw ; a++ ) {
  1404.         i = ( lagometer.frameCount - 1 - a ) & (LAG_SAMPLES - 1);
  1405.         v = lagometer.frameSamples[i];
  1406.         v *= vscale;
  1407.         if ( v > 0 ) {
  1408.             if ( color != 1 ) {
  1409.                 color = 1;
  1410.                 trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] );
  1411.             }
  1412.             if ( v > range ) {
  1413.                 v = range;
  1414.             }
  1415.             trap_R_DrawStretchPic ( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
  1416.         } else if ( v < 0 ) {
  1417.             if ( color != 2 ) {
  1418.                 color = 2;
  1419.                 trap_R_SetColor( g_color_table[ColorIndex(COLOR_BLUE)] );
  1420.             }
  1421.             v = -v;
  1422.             if ( v > range ) {
  1423.                 v = range;
  1424.             }
  1425.             trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
  1426.         }
  1427.     }
  1428.  
  1429.     // draw the snapshot latency / drop graph
  1430.     range = ah / 2;
  1431.     vscale = range / MAX_LAGOMETER_PING;
  1432.  
  1433.     for ( a = 0 ; a < aw ; a++ ) {
  1434.         i = ( lagometer.snapshotCount - 1 - a ) & (LAG_SAMPLES - 1);
  1435.         v = lagometer.snapshotSamples[i];
  1436.         if ( v > 0 ) {
  1437.             if ( lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED ) {
  1438.                 if ( color != 5 ) {
  1439.                     color = 5;    // YELLOW for rate delay
  1440.                     trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] );
  1441.                 }
  1442.             } else {
  1443.                 if ( color != 3 ) {
  1444.                     color = 3;
  1445.                     trap_R_SetColor( g_color_table[ColorIndex(COLOR_GREEN)] );
  1446.                 }
  1447.             }
  1448.             v = v * vscale;
  1449.             if ( v > range ) {
  1450.                 v = range;
  1451.             }
  1452.             trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader );
  1453.         } else if ( v < 0 ) {
  1454.             if ( color != 4 ) {
  1455.                 color = 4;        // RED for dropped snapshots
  1456.                 trap_R_SetColor( g_color_table[ColorIndex(COLOR_RED)] );
  1457.             }
  1458.             trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader );
  1459.         }
  1460.     }
  1461.  
  1462.     trap_R_SetColor( NULL );
  1463.  
  1464.     if ( cg_nopredict.integer || cg_syncronousClients.integer ) {
  1465.         CG_DrawBigString( ax, ay, "snc", 1.0 );
  1466.     }
  1467.  
  1468.     CG_DrawDisconnect();
  1469. }
  1470.  
  1471.  
  1472.  
  1473. /*
  1474. ===============================================================================
  1475.  
  1476. CENTER PRINTING
  1477.  
  1478. ===============================================================================
  1479. */
  1480.  
  1481.  
  1482. /*
  1483. ==============
  1484. CG_CenterPrint
  1485.  
  1486. Called for important messages that should stay in the center of the screen
  1487. for a few moments
  1488. ==============
  1489. */
  1490. void CG_CenterPrint( const char *str, int y, int charWidth ) {
  1491.     char    *s;
  1492.  
  1493.     Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
  1494.  
  1495.     cg.centerPrintTime = cg.time;
  1496.     cg.centerPrintY = y;
  1497.     cg.centerPrintCharWidth = charWidth;
  1498.  
  1499.     // count the number of lines for centering
  1500.     cg.centerPrintLines = 1;
  1501.     s = cg.centerPrint;
  1502.     while( *s ) {
  1503.         if (*s == '\n')
  1504.             cg.centerPrintLines++;
  1505.         s++;
  1506.     }
  1507. }
  1508.  
  1509.  
  1510. /*
  1511. ===================
  1512. CG_DrawCenterString
  1513. ===================
  1514. */
  1515. static void CG_DrawCenterString( void ) {
  1516.     char    *start;
  1517.     int        l;
  1518.     int        x, y, w;
  1519.     float    *color;
  1520.  
  1521.     if ( !cg.centerPrintTime ) {
  1522.         return;
  1523.     }
  1524.  
  1525.     color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value );
  1526.     if ( !color ) {
  1527.         return;
  1528.     }
  1529.  
  1530.     trap_R_SetColor( color );
  1531.  
  1532.     start = cg.centerPrint;
  1533.  
  1534.     y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2;
  1535.  
  1536.     while ( 1 ) {
  1537.         char linebuffer[1024];
  1538.  
  1539.         for ( l = 0; l < 40; l++ ) {
  1540.             if ( !start[l] || start[l] == '\n' ) {
  1541.                 break;
  1542.             }
  1543.             linebuffer[l] = start[l];
  1544.         }
  1545.         linebuffer[l] = 0;
  1546.  
  1547.         w = cg.centerPrintCharWidth * CG_DrawStrlen( linebuffer );
  1548.  
  1549.         x = ( SCREEN_WIDTH - w ) / 2;
  1550.  
  1551.         CG_DrawStringExt( x, y, linebuffer, color, qfalse, qtrue,
  1552.             cg.centerPrintCharWidth, (int)(cg.centerPrintCharWidth * 1.5), 0 );
  1553.  
  1554.         y += cg.centerPrintCharWidth * 1.5;
  1555.  
  1556.         while ( *start && ( *start != '\n' ) ) {
  1557.             start++;
  1558.         }
  1559.         if ( !*start ) {
  1560.             break;
  1561.         }
  1562.         start++;
  1563.     }
  1564.  
  1565.     trap_R_SetColor( NULL );
  1566. }
  1567.  
  1568.  
  1569.  
  1570. /*
  1571. ================================================================================
  1572.  
  1573. CROSSHAIR
  1574.  
  1575. ================================================================================
  1576. */
  1577.  
  1578.  
  1579. /*
  1580. =================
  1581. CG_DrawCrosshair
  1582. =================
  1583. */
  1584. static void CG_DrawCrosshair(void) {
  1585.     float        w, h;
  1586.     qhandle_t    hShader;
  1587.     float        f;
  1588.     float        x, y;
  1589.  
  1590.     if ( !cg_drawCrosshair.integer ) {
  1591.         return;
  1592.     }
  1593.  
  1594.     if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) {
  1595.         return;
  1596.     }
  1597.  
  1598.     if ( cg.renderingThirdPerson ) {
  1599.         return;
  1600.     }
  1601.  
  1602.     // set color based on health
  1603.     if ( cg_crosshairHealth.integer ) {
  1604.         vec4_t        hcolor;
  1605.  
  1606.         CG_ColorForHealth( hcolor );
  1607.         trap_R_SetColor( hcolor );
  1608.     } else {
  1609.         trap_R_SetColor( NULL );
  1610.     }
  1611.  
  1612.     w = h = cg_crosshairSize.value;
  1613.  
  1614.     // pulse the size of the crosshair when picking up items
  1615.     f = cg.time - cg.itemPickupBlendTime;
  1616.     if ( f > 0 && f < ITEM_BLOB_TIME ) {
  1617.         f /= ITEM_BLOB_TIME;
  1618.         w *= ( 1 + f );
  1619.         h *= ( 1 + f );
  1620.     }
  1621.  
  1622.     x = cg_crosshairX.integer;
  1623.     y = cg_crosshairY.integer;
  1624.     CG_AdjustFrom640( &x, &y, &w, &h );
  1625.  
  1626.     hShader = cgs.media.crosshairShader[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ];
  1627.  
  1628.     trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (cg.refdef.width - w), 
  1629.         y + cg.refdef.y + 0.5 * (cg.refdef.height - h), 
  1630.         w, h, 0, 0, 1, 1, hShader );
  1631. }
  1632.  
  1633.  
  1634.  
  1635. /*
  1636. =================
  1637. CG_ScanForCrosshairEntity
  1638. =================
  1639. */
  1640. static void CG_ScanForCrosshairEntity( void ) {
  1641.     trace_t        trace;
  1642.     vec3_t        start, end;
  1643.     int            content;
  1644.  
  1645.     VectorCopy( cg.refdef.vieworg, start );
  1646.     VectorMA( start, 8192, cg.refdef.viewaxis[0], end );
  1647.  
  1648.     CG_Trace( &trace, start, vec3_origin, vec3_origin, end, 
  1649.         cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY );
  1650.     if ( trace.entityNum >= MAX_CLIENTS ) {
  1651.         return;
  1652.     }
  1653.  
  1654.     // if the player is in fog, don't show it
  1655.     content = trap_CM_PointContents( trace.endpos, 0 );
  1656.     if ( content & CONTENTS_FOG ) {
  1657.         return;
  1658.     }
  1659.  
  1660.     // if the player is invisible, don't show it
  1661.     if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_INVIS ) ) {
  1662.         return;
  1663.     }
  1664.  
  1665.     // update the fade timer
  1666.     cg.crosshairClientNum = trace.entityNum;
  1667.     cg.crosshairClientTime = cg.time;
  1668. }
  1669.  
  1670.  
  1671. /*
  1672. =====================
  1673. CG_DrawCrosshairNames
  1674. =====================
  1675. */
  1676. static void CG_DrawCrosshairNames( void ) {
  1677.     float        *color;
  1678.     char        *name;
  1679.     float        w;
  1680.  
  1681.     if ( !cg_drawCrosshair.integer ) {
  1682.         return;
  1683.     }
  1684.     if ( !cg_drawCrosshairNames.integer ) {
  1685.         return;
  1686.     }
  1687.     if ( cg.renderingThirdPerson ) {
  1688.         return;
  1689.     }
  1690.  
  1691.     // scan the known entities to see if the crosshair is sighted on one
  1692.     CG_ScanForCrosshairEntity();
  1693.  
  1694.     // draw the name of the player being looked at
  1695.     color = CG_FadeColor( cg.crosshairClientTime, 1000 );
  1696.     if ( !color ) {
  1697.         trap_R_SetColor( NULL );
  1698.         return;
  1699.     }
  1700.  
  1701.     name = cgs.clientinfo[ cg.crosshairClientNum ].name;
  1702.     w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH;
  1703.     CG_DrawBigString( 320 - w / 2, 170, name, color[3] * 0.5 );
  1704.  
  1705.     trap_R_SetColor( NULL );
  1706. }
  1707.  
  1708.  
  1709.  
  1710. //==============================================================================
  1711.  
  1712. /*
  1713. =================
  1714. CG_DrawSpectator
  1715. =================
  1716. */
  1717. static void CG_DrawSpectator(void) {
  1718.     CG_DrawBigString(320 - 9 * 8, 440, "SPECTATOR", 1.0F);
  1719.     if ( cgs.gametype == GT_TOURNAMENT ) {
  1720.         CG_DrawBigString(320 - 15 * 8, 460, "waiting to play", 1.0F);
  1721.     }
  1722.     if ( cgs.gametype == GT_TEAM || cgs.gametype == GT_CTF ) {
  1723.         CG_DrawBigString(320 - 25 * 8, 460, "use the TEAM menu to play", 1.0F);
  1724.     }
  1725. }
  1726.  
  1727. /*
  1728. =================
  1729. CG_DrawVote
  1730. =================
  1731. */
  1732. static void CG_DrawVote(void) {
  1733.     char    *s;
  1734.     int        sec;
  1735.  
  1736.     if ( !cgs.voteTime ) {
  1737.         return;
  1738.     }
  1739.  
  1740.     // play a talk beep whenever it is modified
  1741.     if ( cgs.voteModified ) {
  1742.         cgs.voteModified = qfalse;
  1743.         trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  1744.     }
  1745.  
  1746.     sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000;
  1747.     if ( sec < 0 ) {
  1748.         sec = 0;
  1749.     }
  1750.     s = va("VOTE(%i):%s yes(F1):%i no(F2):%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo );
  1751.     CG_DrawSmallString( 0, 58, s, 1.0F );
  1752. }
  1753.  
  1754. /*
  1755. =================
  1756. CG_DrawIntermission
  1757. =================
  1758. */
  1759. static void CG_DrawIntermission( void ) {
  1760.     if ( cgs.gametype == GT_SINGLE_PLAYER ) {
  1761.         CG_DrawCenterString();
  1762.         return;
  1763.     }
  1764.  
  1765.     cg.scoreFadeTime = cg.time;
  1766.     CG_DrawScoreboard();
  1767. }
  1768.  
  1769. /*
  1770. =================
  1771. CG_DrawFollow
  1772. =================
  1773. */
  1774. static qboolean CG_DrawFollow( void ) {
  1775.     float        x;
  1776.     vec4_t        color;
  1777.     const char    *name;
  1778.  
  1779.     if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
  1780.         return qfalse;
  1781.     }
  1782.     color[0] = 1;
  1783.     color[1] = 1;
  1784.     color[2] = 1;
  1785.     color[3] = 1;
  1786.  
  1787.  
  1788.     CG_DrawBigString( 320 - 9 * 8, 24, "following", 1.0F );
  1789.  
  1790.     name = cgs.clientinfo[ cg.snap->ps.clientNum ].name;
  1791.  
  1792.     x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( name ) );
  1793.  
  1794.     CG_DrawStringExt( x, 40, name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
  1795.  
  1796.     return qtrue;
  1797. }
  1798.  
  1799.  
  1800.  
  1801. /*
  1802. =================
  1803. CG_DrawAmmoWarning
  1804. =================
  1805. */
  1806. static void CG_DrawAmmoWarning( void ) {
  1807.     const char    *s;
  1808.     int            w;
  1809.  
  1810.     if ( cg_drawAmmoWarning.integer == 0 ) {
  1811.         return;
  1812.     }
  1813.  
  1814.     if ( !cg.lowAmmoWarning ) {
  1815.         return;
  1816.     }
  1817.  
  1818.     if ( cg.lowAmmoWarning == 2 ) {
  1819.         s = "OUT OF AMMO";
  1820.     } else {
  1821.         s = "LOW AMMO WARNING";
  1822.     }
  1823.     w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
  1824.     CG_DrawBigString(320 - w / 2, 64, s, 1.0F);
  1825. }
  1826.  
  1827. /*
  1828. =================
  1829. CG_DrawWarmup
  1830. =================
  1831. */
  1832. static void CG_DrawWarmup( void ) {
  1833.     int            w;
  1834.     int            sec;
  1835.     int            i;
  1836.     clientInfo_t    *ci1, *ci2;
  1837.     int            cw;
  1838.     const char    *s;
  1839.  
  1840.     sec = cg.warmup;
  1841.     if ( !sec ) {
  1842.         return;
  1843.     }
  1844.  
  1845.     if ( sec < 0 ) {
  1846.         s = "Waiting for players";        
  1847.         w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
  1848.         CG_DrawBigString(320 - w / 2, 40, s, 1.0F);
  1849.         cg.warmupCount = 0;
  1850.         return;
  1851.     }
  1852.  
  1853.     if (cgs.gametype == GT_TOURNAMENT) {
  1854.         // find the two active players
  1855.         ci1 = NULL;
  1856.         ci2 = NULL;
  1857.         for ( i = 0 ; i < cgs.maxclients ; i++ ) {
  1858.             if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE ) {
  1859.                 if ( !ci1 ) {
  1860.                     ci1 = &cgs.clientinfo[i];
  1861.                 } else {
  1862.                     ci2 = &cgs.clientinfo[i];
  1863.                 }
  1864.             }
  1865.         }
  1866.  
  1867.         if ( ci1 && ci2 ) {
  1868.             s = va( "%s vs %s", ci1->name, ci2->name );
  1869.             w = CG_DrawStrlen( s );
  1870.             if ( w > 640 / GIANT_WIDTH ) {
  1871.                 cw = 640 / w;
  1872.             } else {
  1873.                 cw = GIANT_WIDTH;
  1874.             }
  1875.             CG_DrawStringExt( 320 - w * cw/2, 20,s, colorWhite, 
  1876.                     qfalse, qtrue, cw, (int)(cw * 1.5), 0 );
  1877.         }
  1878.     } else {
  1879.         if ( cgs.gametype == GT_FFA ) {
  1880.             s = "Free For All";
  1881.         } else if ( cgs.gametype == GT_TEAM ) {
  1882.             s = "Team Deathmatch";
  1883.         } else if ( cgs.gametype == GT_CTF ) {
  1884.             s = "Capture the Flag";
  1885.         } else {
  1886.             s = "";
  1887.         }
  1888.         w = CG_DrawStrlen( s );
  1889.         if ( w > 640 / GIANT_WIDTH ) {
  1890.             cw = 640 / w;
  1891.         } else {
  1892.             cw = GIANT_WIDTH;
  1893.         }
  1894.         CG_DrawStringExt( 320 - w * cw/2, 20,s, colorWhite, 
  1895.                 qfalse, qtrue, cw, (int)(cw * 1.5), 0 );
  1896.     }
  1897.  
  1898.     sec = ( sec - cg.time ) / 1000;
  1899.     if ( sec < 0 ) {
  1900.         sec = 0;
  1901.     }
  1902.     s = va( "Starts in: %i", sec + 1 );
  1903.     if ( sec != cg.warmupCount ) {
  1904.         cg.warmupCount = sec;
  1905.         switch ( sec ) {
  1906.         case 0:
  1907.             trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER );
  1908.             break;
  1909.         case 1:
  1910.             trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER );
  1911.             break;
  1912.         case 2:
  1913.             trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER );
  1914.             break;
  1915.         default:
  1916.             break;
  1917.         }
  1918.     }
  1919.     switch ( cg.warmupCount ) {
  1920.     case 0:
  1921.         cw = 28;
  1922.         break;
  1923.     case 1:
  1924.         cw = 24;
  1925.         break;
  1926.     case 2:
  1927.         cw = 20;
  1928.         break;
  1929.     default:
  1930.         cw = 16;
  1931.         break;
  1932.     }
  1933.  
  1934.     w = CG_DrawStrlen( s );
  1935.     CG_DrawStringExt( 320 - w * cw/2, 70, s, colorWhite, 
  1936.             qfalse, qtrue, cw, (int)(cw * 1.5), 0 );
  1937. }
  1938.  
  1939. //==================================================================================
  1940.  
  1941. /*
  1942. =================
  1943. CG_Draw2D
  1944. =================
  1945. */
  1946. static void CG_Draw2D( void ) {
  1947.  
  1948.     // if we are taking a levelshot for the menu, don't draw anything
  1949.     if ( cg.levelShot ) {
  1950.         return;
  1951.     }
  1952.  
  1953.     if ( cg_draw2D.integer == 0 ) {
  1954.         return;
  1955.     }
  1956.  
  1957.     if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
  1958.         CG_DrawIntermission();
  1959.         return;
  1960.     }
  1961.  
  1962.     if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
  1963.         CG_DrawSpectator();
  1964.         CG_DrawCrosshair();
  1965.         CG_DrawCrosshairNames();
  1966.     } else {
  1967.         // don't draw any status if dead
  1968.         if ( cg.snap->ps.stats[STAT_HEALTH] > 0 ) {
  1969.             CG_DrawStatusBar();
  1970.             CG_DrawAmmoWarning();
  1971.             CG_DrawCrosshair();
  1972.             CG_DrawCrosshairNames();
  1973.             CG_DrawWeaponSelect();
  1974.             CG_DrawHoldableItem();
  1975.             CG_DrawReward();
  1976.         }
  1977.         if ( cgs.gametype >= GT_TEAM ) {
  1978.             CG_DrawTeamInfo();
  1979.         }
  1980.     }
  1981.  
  1982.     CG_DrawVote();
  1983.  
  1984.     CG_DrawLagometer();
  1985.  
  1986.     CG_DrawUpperRight();
  1987.  
  1988.     CG_DrawLowerRight();
  1989.  
  1990.     CG_DrawLowerLeft();
  1991.  
  1992.     if ( !CG_DrawFollow() ) {
  1993.         CG_DrawWarmup();
  1994.     }
  1995.  
  1996.     // don't draw center string if scoreboard is up
  1997.     if ( !CG_DrawScoreboard() ) {
  1998.         CG_DrawCenterString();
  1999.     }
  2000. }
  2001.  
  2002.  
  2003. /*
  2004. =====================
  2005. CG_DrawActive
  2006.  
  2007. Perform all drawing needed to completely fill the screen
  2008. =====================
  2009. */
  2010. void CG_DrawActive( stereoFrame_t stereoView ) {
  2011.     float        separation;
  2012.     vec3_t        baseOrg;
  2013.  
  2014.     // optionally draw the info screen instead
  2015.     if ( !cg.snap ) {
  2016.         CG_DrawInformation();
  2017.         return;
  2018.     }
  2019.  
  2020.     // optionally draw the tournement scoreboard instead
  2021.     if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR &&
  2022.         ( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) {
  2023.         CG_DrawTourneyScoreboard();
  2024.         return;
  2025.     }
  2026.  
  2027.     switch ( stereoView ) {
  2028.     case STEREO_CENTER:
  2029.         separation = 0;
  2030.         break;
  2031.     case STEREO_LEFT:
  2032.         separation = -cg_stereoSeparation.value / 2;
  2033.         break;
  2034.     case STEREO_RIGHT:
  2035.         separation = cg_stereoSeparation.value / 2;
  2036.         break;
  2037.     default:
  2038.         separation = 0;
  2039.         CG_Error( "CG_DrawActive: Undefined stereoView" );
  2040.     }
  2041.  
  2042.  
  2043.     // clear around the rendered view if sized down
  2044.     CG_TileClear();
  2045.  
  2046.     // offset vieworg appropriately if we're doing stereo separation
  2047.     VectorCopy( cg.refdef.vieworg, baseOrg );
  2048.     if ( separation != 0 ) {
  2049.         VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[1], cg.refdef.vieworg );
  2050.     }
  2051.  
  2052.     // draw 3D view
  2053.     trap_R_RenderScene( &cg.refdef );
  2054.  
  2055.     // restore original viewpoint if running stereo
  2056.     if ( separation != 0 ) {
  2057.         VectorCopy( baseOrg, cg.refdef.vieworg );
  2058.     }
  2059.  
  2060.     // draw status bar and other floating elements
  2061.     CG_Draw2D();
  2062. }
  2063.  
  2064.  
  2065.  
  2066.